package com.xbongbong.paas.help;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.enums.ConditionEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.pro.enums.PackageTypeEnum;
import com.xbongbong.pro.permission.pojo.dto.VerifyUpdatePermissionDTO;
import com.xbongbong.pro.permission.pojo.dto.VerifyViewPermissionDTO;
import com.xbongbong.pro.sharepermission.pojo.SharePermissionConditionFilters;
import com.xbongbong.pro.sharepermission.pojo.SharePermissionConditionPojo;
import com.xbongbong.pro.sharepermission.pojo.SharePermissionJointConditionPojo;
import com.xbongbong.rule.builder.XbbRuleEngineBuilder;
import com.xbongbong.rule.engine.XbbRuleEngine;
import com.xbongbong.rule.rules.DefaultXbbRule;
import com.xbongbong.rule.rules.XbbCondition;
import com.xbongbong.rule.rules.XbbRule;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.SharePermissionSetUserEntity;
import com.xbongbong.saas.domain.entity.ShareRuleEntity;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.sys.domain.entity.CompanyConfigEntity;
import com.xbongbong.sys.help.PackageHelp;
import com.xbongbong.sys.model.CompanyConfigModel;
import com.xbongbong.sys.model.SharePermissionSetUserModel;
import com.xbongbong.sys.model.ShareRuleModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Component
public class SharePermissionHelp {

    private static final Logger LOG = LoggerFactory.getLogger(SharePermissionHelp.class);

    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private PackageHelp packageHelp;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private SharePermissionSetUserModel sharePermissionSetUserModel;
    @Resource
    private ShareRuleModel shareRuleModel;

    /**
     * 校验共享查看权限(采用ElasticSearch)
     * 传入dataId和条件,查出1条则为true,查出0条则为false
     *
     * @param verifyViewPermissionDTO
     * @return
     */
    private static boolean verifySharePermission4QueryWithElasticSearch(VerifyViewPermissionDTO verifyViewPermissionDTO) {
        //备用方案,待开发
        return false;
    }

    /**
     * 校验共享编辑权限(规则引擎模式)
     *
     * @param verifyUpdatePermissionDTO
     * @return
     */
    public boolean verifySharePermission4EditWithRuleEngine(VerifyUpdatePermissionDTO verifyUpdatePermissionDTO) throws XbbException {
        PaasFormDataEntityExt paasFormDataEntityExt = verifyUpdatePermissionDTO.getPaasFormDataEntityExt();
        String corpid = paasFormDataEntityExt.getCorpid();
        Long formId = paasFormDataEntityExt.getFormId();
        String userId = verifyUpdatePermissionDTO.getLoginUser().getUserId();

        //非旗舰版不校验
        Integer feeType = packageHelp.getFeeType(corpid);
        if (!Objects.equals(feeType, PackageTypeEnum.ULTIMATE.getType())) {
            return false;
        }
        if (!checkSharePermissionWhite(corpid)) {
            return false;
        }
        //获取表单的共享权限规则
        List<ShareRuleEntity> ruleEntities = Optional.ofNullable(verifyUpdatePermissionDTO.getRuleEntities()).orElse(getRuleEntities(corpid, formId, userId));
        if (CollectionsUtil.isEmpty(ruleEntities)) {
            return false;
        }
        Map<Long, ShareRuleEntity> ruleMap = ruleEntities.stream().collect(Collectors.toMap(ShareRuleEntity::getId, ruleEntity -> ruleEntity));
        //格式化表单数据
        formatFormData(paasFormDataEntityExt);
        //规则引擎构建
        XbbRuleEngine<JSONObject> xbbRuleEngine = new XbbRuleEngineBuilder()
                .setSkipOnFirstAppliedRule(false)
                .setXbbRules(covertRuleEntity2List(ruleEntities))
                .setData(paasFormDataEntityExt.getData())
                .build();
        //启动匹配
        List<XbbRule<JSONObject>> result = xbbRuleEngine.fire();
        //若引擎返回规则,且其中一组规则是可编辑权限,则返回true
        return result.stream().anyMatch(xbbRule -> {
            String ruleId = xbbRule.getRuleId().split(StringConstant.UNDER_LINE)[0];
            ShareRuleEntity shareRuleEntity = ruleMap.get(Long.valueOf(ruleId));
            return Objects.equals(shareRuleEntity.getPermission(), BasicConstant.ONE);
        });
    }

    /**
     * 校验共享查看权限(采用规则引擎)
     *
     * @param verifyViewPermissionDTO
     * @return
     */
    public boolean verifySharePermission4QueryWithRuleEngine(VerifyViewPermissionDTO verifyViewPermissionDTO) throws XbbException {
        PaasFormDataEntityExt paasFormDataEntityExt = verifyViewPermissionDTO.getPaasFormDataEntityExt();
        String corpid = paasFormDataEntityExt.getCorpid();
        Long formId = paasFormDataEntityExt.getFormId();
        String userId = verifyViewPermissionDTO.getLoginUser().getUserId();

        //非旗舰版不校验
        Integer feeType = packageHelp.getFeeType(corpid);
        if (!Objects.equals(feeType, PackageTypeEnum.ULTIMATE.getType())) {
            return false;
        }
        if (!checkSharePermissionWhite(corpid)) {
            return false;
        }
        //数据库获取相关规则
        List<ShareRuleEntity> ruleEntities = getRuleEntities(corpid, formId, userId);
        if (CollectionsUtil.isEmpty(ruleEntities)) {
            return false;
        }
        //格式化表单数据
        formatFormData(paasFormDataEntityExt);

        //规则引擎构建
        XbbRuleEngine<JSONObject> xbbRuleEngine = new XbbRuleEngineBuilder()
                .setSkipOnFirstAppliedRule(true)
                .setXbbRules(covertRuleEntity2List(ruleEntities))
                .setData(paasFormDataEntityExt.getData())
                .build();
        //启动匹配
        int size = xbbRuleEngine.fire().size();
        //若引擎返回规则,则说明命中了规则,返回true
        return size == BasicConstant.ONE;
    }

    /**
     * 判断该公司是否属于共享权限白名单公司
     *
     * @param corpid
     * @return
     */
    public boolean checkSharePermissionWhite(String corpid) {
        Set<String> whiteSet = new HashSet<>();
        String alias = CompanyConfigEnum.SHARE_PERMISSION_WHITE_CORPIDS.getAlias();
        String redisKey = RedisPrefixConstant.SHARE_PERMISSION_WHITE_CORPIDS;
        String value = paasRedisHelper.getValue(redisKey, alias);
        if (value == null) {
            //读出白名单配置
            CompanyConfigEntity companyConfig = companyConfigModel.getByConfigAlias(alias, "0");
            if (companyConfig != null) {
                whiteSet.addAll(JsonHelperUtil.parseArray(companyConfig.getConfigValue(), String.class));
                //存入缓存
                paasRedisHelper.setValue(redisKey, alias, JSONArray.toJSONString(whiteSet), TimeConstant.SECONDS_PER_DAY);
            }
        } else {
            whiteSet.addAll(JsonHelperUtil.parseArray(value, String.class));
        }
        return whiteSet.contains(corpid);
    }

    /**
     * 获取到当前表单登录人的相关规则
     *
     * @param corpid
     * @param formId
     * @param userId
     * @return
     */
    public List<ShareRuleEntity> getRuleEntities(String corpid, Long formId, String userId) {
        return getRuleEntities(corpid, formId, userId, getOpenFieldSet(corpid, formId));
    }

    /**
     * 获取到当前表单登录人的相关规则
     *
     * @param corpid
     * @param formId
     * @param userId
     * @return
     */
    private List<ShareRuleEntity> getRuleEntities(String corpid, Long formId, String userId, Set<String> fieldSet) {
        //获取相关规则
        Map<String, Object> params = new HashMap<>(BasicConstant.FOUR);
        params.put(StringConstant.CORPID, corpid);
        params.put(StringConstant.FORM_ID, formId);
        params.put("userIdIn", Collections.singletonList(userId));
        params.put(StringConstant.DEL, 0);
        List<SharePermissionSetUserEntity> setUserEntities = sharePermissionSetUserModel.findEntitys(params);
        List<Long> ruleIds = setUserEntities.stream().map(SharePermissionSetUserEntity::getShareRuleId).distinct().collect(Collectors.toList());
        List<ShareRuleEntity> entities = shareRuleModel.getByIdIn(ruleIds, corpid);
        //过滤失效的规则
        entities.removeIf(ruleEntity -> {
            List<SharePermissionConditionFilters> filters = ruleEntity.getCondition().toJavaObject(SharePermissionJointConditionPojo.class).getFilters();
            for (SharePermissionConditionFilters filter : filters) {
                for (SharePermissionConditionPojo condition : filter.getFilter()) {
                    if (!fieldSet.contains(condition.getAttr()) && !fieldSet.contains(condition.getAttr() + StringConstant.POINT + condition.getSubAttr())) {
                        return true;
                    }
                }
            }
            return false;
        });
        return entities;
    }

    /**
     * 获取已开启字段集合
     *
     * @param corpid
     * @param formId
     * @return
     */
    private Set<String> getOpenFieldSet(String corpid, Long formId) {
        try {
            //查询并移除未开启的解释
            PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByFormId(formId, corpid);
            if (Objects.isNull(paasFormExplainEntity)) {
                return new HashSet<>();
            }
            List<FieldAttrEntity> explainList = JSON.parseArray(paasFormExplainEntity.getExplains(), FieldAttrEntity.class);
            explainList.removeIf(explain -> !Objects.equals(explain.getIsOpen(), BasicConstant.ONE));
            //获取字段集合MAP,包括子表单内的attr,用attr.subAttr
            Map<String, FieldAttrEntity> explainMap = new LinkedHashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (FieldAttrEntity fieldAttrEntity : explainList) {
                String attr = fieldAttrEntity.getAttr();
                explainMap.put(attr, fieldAttrEntity);
                if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.SUB_FORM.getType())) {
                    List<? extends FieldAttrEntity> subExplainList = fieldAttrEntity.getSubForm().getItems();
                    for (FieldAttrEntity subAttrEntity : subExplainList) {
                        String key = attr + StringConstant.POINT + subAttrEntity.getAttr();
                        subAttrEntity.setAttrName(fieldAttrEntity.getAttrName() + StringConstant.POINT + subAttrEntity.getAttrName());
                        explainMap.put(key, subAttrEntity);
                    }
                }
            }
            return explainMap.keySet();
        } catch (Exception e) {
            LOG.error("SharePermissionHelp.getOpenFieldSet error", e);
            return new HashSet<>();
        }
    }

    /**
     * 转换规则实体为规则列表
     *
     * @param ruleEntities
     * @return
     */
    private List<XbbRule<JSONObject>> covertRuleEntity2List(List<ShareRuleEntity> ruleEntities) {
        AtomicInteger atomicInteger = new AtomicInteger(BasicConstant.ONE);
        List<XbbRule<JSONObject>> ruleList = new ArrayList<>();
        ruleEntities.forEach(ruleEntity -> {
            List<SharePermissionConditionFilters> filters = ruleEntity.getCondition().toJavaObject(SharePermissionJointConditionPojo.class).getFilters();
            filters.forEach(filter -> {
                DefaultXbbRule<JSONObject> xbbRule = new DefaultXbbRule<>();
                xbbRule.setRuleId(ruleEntity.getId() + StringConstant.UNDER_LINE + atomicInteger.getAndIncrement());
                //变成List<XbbCondition>
                List<SharePermissionConditionPojo> subFilter = filter.getFilter();
                List<XbbCondition> subList = subFilter.stream().map(condition -> {
                    XbbCondition xbbCondition = new XbbCondition();
                    xbbCondition.setValue(condition.getValue());
                    xbbCondition.setAttr(condition.getAttr() + (Objects.nonNull(condition.getSubAttr()) ? StringConstant.POINT + condition.getSubAttr() : ""));
                    xbbCondition.setFieldType(condition.getFieldType());
                    xbbCondition.setConditionEnum(ConditionEnum.getConditionEnum(condition.getSymbol()));
                    return xbbCondition;
                }).collect(Collectors.toList());
                xbbRule.setConditionItems(subList);
                ruleList.add(xbbRule);
            });
        });
        return ruleList;
    }

    /**
     * 格式化表单数据
     *
     * @param paasFormDataEntityExt
     */
    private void formatFormData(PaasFormDataEntityExt paasFormDataEntityExt) {
        //拼装系统数据进data
        setSystemDataIntoData(paasFormDataEntityExt);
        //平铺子表单数据进data
        covertSubFormDataIntoData(paasFormDataEntityExt);
    }

    /**
     * 将表单实体内的系统字段放入data内
     *
     * @param entityExt 表单实体
     */
    private void setSystemDataIntoData(PaasFormDataEntityExt entityExt) {
        JSONObject data = entityExt.getData();
        data.put(FieldTypeEnum.CREATORID.getAlias(), entityExt.getCreatorId());
        data.put(FieldTypeEnum.SERIALNO.getAlias(), entityExt.getSerialNo());
        data.put(FieldTypeEnum.UPDATETIME.getAlias(), entityExt.getUpdateTime());
        data.put(FieldTypeEnum.ADDTIME.getAlias(), entityExt.getAddTime());
        data.put(FieldTypeEnum.DATAID.getAlias(), entityExt.getId());
        data.put(StringConstant.CORPID, entityExt.getCorpid());
        data.put(StringConstant.FORM_ID, entityExt.getFormId());
        data.put(FieldTypeEnum.DEPARTMENTID.getAlias(), entityExt.getDepartmentId());
    }

    /**
     * 将表单实体内的子表单字段平铺处理
     *
     * @param paasFormDataEntityExt
     */
    private void covertSubFormDataIntoData(PaasFormDataEntityExt paasFormDataEntityExt) {
        JSONObject data = paasFormDataEntityExt.getData();
        if (data.containsKey(FieldTypeEnum.SUB_FORM.getEsalias() + StringConstant.POINT)){
            //如果已经处理过了,就不二次处理了
            return;
        }
        JSONObject subFormData = new JSONObject();
        data.forEach((attr, values) -> {
            if (attr.contains(FieldTypeEnum.SUB_FORM.getEsalias()) && !attr.contains(StringConstant.POINT)) {
                if (Objects.nonNull(values)) {
                    Map<String, JSONArray> map = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    JSONArray valueArray = (JSONArray) values;
                    valueArray.forEach(value -> {
                        JSONObject subValue = (JSONObject) value;
                        subValue.forEach((k, v) -> {
                            JSONArray item = map.getOrDefault(k, new JSONArray());
                            item.add(v);
                            map.put(k, item);
                        });
                    });
                    map.forEach((k, v) -> subFormData.put(attr + StringConstant.POINT + k, v));
                }

            }
        });
        data.putAll(subFormData);
    }
}
